/*
 * Created on 24/Fev/2005
 */
package edu.mapi.ir.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Vector;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileFilter;


/**
 * Class that provides graphical interface utilities
 * @author Ze Carlos
 * @author Paulo Dias
 * @author Sergio Fraga
 */
class ImageLoader {

	Image image;

	public ImageLoader(String imageUrl) {

		if (imageUrl == null) {
			image = null;
			return;
		}
		
		try {
			image = new ImageIcon(this.getClass().getClassLoader().getResource(imageUrl)).getImage();
		}
		catch (Exception e) {
			image = new ImageIcon(imageUrl).getImage();
		}
	}

	public Image getImage() {
		if (image == null)
			return null;

		if (image.getWidth(null) < 0)
			return null;
		else
			return image;
	}
}

/**
 * @author Ze Carlos
 * @author Paulo Dias
 */
public class GuiUtils {

	public static String CLIP_ERROR = "sounds/error.wav";
	public static String CLIP_WARNING = "sounds/warning.wav";
	public static String CLIP_COMPLETE = "sounds/complete.wav";

	public static NumberFormat getNeptusDecimalFormat() {
		NumberFormat df = DecimalFormat.getInstance(Locale.US);
		df.setGroupingUsed(false);
		df.setMaximumFractionDigits(15);
		return df;
	}

	public static NumberFormat getNeptusIntegerFormat() {
		NumberFormat df = DecimalFormat.getInstance(Locale.US);
		df.setGroupingUsed(false);
		df.setMaximumFractionDigits(0);
		df.setMinimumFractionDigits(0);
		df.setParseIntegerOnly(true);
		return df;
	}

	static Hashtable<Integer, NumberFormat>nformats = new Hashtable<Integer, NumberFormat>();

	public static NumberFormat getNeptusDecimalFormat(int fractionDigits) {

		if (nformats.containsKey(fractionDigits))
			return nformats.get(fractionDigits);

		NumberFormat df = getNeptusDecimalFormat();
		df.setMaximumFractionDigits(fractionDigits);
		df.setMinimumFractionDigits(fractionDigits);
		nformats.put(fractionDigits, df);
		return df;
	}

	/**
	 * Centers the given <b>Frame</b> in the user screen
	 * @param window A Frame to be centered in the user screen
	 */
	public static void centerOnScreen(Window window) {
		Dimension size = window.getSize();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (scr.width - size.width) / 2;
		int y = (scr.height - size.height) / 2;
		window.setBounds(x, y, size.width, size.height);
	}


	/**
	 * Centers the given <b>Frame</b> in the user screen
	 * @param window A Frame to be at right top corner on user screen
	 */
	public static void rightTopScreen(Window window) {
		Dimension size = window.getSize();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (scr.width - size.width);

		window.setBounds(x, 0, size.width, size.height);
	}


	/**
	 * Centers the given <b>Frame</b> in the user screen
	 * @param window A Frame to be at right center corner on user screen
	 */
	public static void rightCenterScreen(Window window) {
		Dimension size = window.getSize();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (scr.width - size.width);
		int y = (scr.height - size.height) / 2;
		window.setBounds(x, y, size.width, size.height);
	}

	/**
	 * Centers the given <b>Frame</b> in the user screen
	 * @param window A Frame to be at left center corner on user screen
	 */
	public static void leftCenterScreen(Window window) {
		Dimension size = window.getSize();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		//int x = (scr.width - size.width);
		int y = (scr.height - size.height) / 2;
		window.setBounds(0, y, size.width, size.height);
	}

	/**
	 * Centers the given <b>Frame</b> in the user screen
	 * @param window A Frame to be at left bottom corner on user screen
	 */
	public static void leftBottomScreen(Window window) {
		Dimension size = window.getSize();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (0);
		int y = (scr.height - size.height);
		window.setBounds(x, y, size.width, size.height);
	}

	
	/**
	 * Centers the given <b>Frame</b> in the user screen
	 * @param window A Frame to be at left top corner on user screen
	 */
	public static void leftTopScreen(Window window) {
		Dimension size = window.getSize();
		//Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (0);
		int y = (0);
		window.setBounds(x, y, size.width, size.height);
	}


	/**
	 * Place the given <b>Frame</b> at the bottom of parent window
	 * @param window A Frame to be at left bottom corner of parent window
	 */
	public static void bottomParent(Window window, Window parent) {
		Rectangle pos =parent.getBounds();
		Dimension size = window.getSize();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		//Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (pos.x);
		int y = (pos.y+pos.height);
		if(y+size.height>scr.height)
			window.setBounds(x, scr.height-size.height, pos.width, size.height);
		else
			window.setBounds(x, y, pos.width, size.height);	
	}

	/**
	 *
	 * @param win
	 * @param parent
	 */
	public static void southEastParent(Window window, Window parent, int maxwhith, int maxheight)
	{
		Rectangle pos = parent.getBounds();
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
		int x = (pos.x + pos.width);
		int y = (pos.y + pos.height);
		int sizex = scr.width - x;
		int sizey = scr.height - y;
		if(sizex > maxwhith)
			sizex = maxwhith;
		if(sizey > maxheight)
			sizey = maxheight;
		
		if(sizex<200) //200 min size X
		{
				sizex+=200;
				x-=200;
		}
		if(sizey<100) // 100 min size y 
		{
			sizey+=100;
			y-=100;
		}
		if(x>scr.width)
			x-=200;
		
		if(y>scr.height)
			x-=100;
		
		window.setBounds(x, y, sizex, sizey);
	}
	
	public static JFrame testFrame(JComponent component) {
		return testFrame(component, "testing "+component.getClass().getSimpleName());
	}
	
	public static JFrame testFrame(JComponent component, String title) {
		JFrame frame = new JFrame(title);
		frame.setLayout(new BorderLayout());
		frame.getContentPane().add(component, BorderLayout.CENTER);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(400,441);
		centerOnScreen(frame);
		frame.setVisible(true);
		return frame;
	}

	/**
	 * Gets the current screen resolution in the form of a <b>Dimension</b>
	 * @return The current screen resolution
	 */
	public static Dimension getScreenDimension() {
		return Toolkit.getDefaultToolkit().getScreenSize();		
	}

	/**
	 * Gets the number of pixels that can be seen in a single line 
	 * in the user screen (AKA the screen horizontal resolution).
	 * @return The horizontal resolution of the screen in pixels
	 */
	public static int getScreenWidth() {
		return Toolkit.getDefaultToolkit().getScreenSize().width;
	}


	/**
	 * Given a file wich is an AudioClip (valid formats are .wav and .au), this method will play the clip
	 * @param clipFilename The audioclip to be played
	 */
	public static void playAudioClip(String clipFilename) {

		Clip m_clip;

		AudioInputStream audioInputStream = null;
		try {
			audioInputStream = AudioSystem.getAudioInputStream(new FileInputStream(clipFilename));

			if (audioInputStream != null) {
				AudioFormat	format = audioInputStream.getFormat();
				DataLine.Info info = new DataLine.Info(Clip.class, format);
				m_clip = (Clip) AudioSystem.getLine(info);				
				m_clip.open(audioInputStream);
				m_clip.start();
			}					
			else {
				System.out.println("ClipPlayer.<init>(): can't get data from file " + clipFilename);
			}
		}
		catch (LineUnavailableException lineException) {
			Toolkit.getDefaultToolkit().beep();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Gets the number of pixels that can be seen in a single column 
	 * in the user screen (AKA the screen vertical resolution).
	 * @return The vertical resolution of the screen in pixels
	 */
	public static int getScreenHeight() {
		return Toolkit.getDefaultToolkit().getScreenSize().height;
	}

	/**
	 * Loads and returns an <b>Image</b>
	 * @param imageURL The URL from where the image is to be loaded
	 * @return The loaded <b>Image</b> or <b>null</b> if the image does not exist
	 */
	public static Image getImage(String imageURL) {
		
		ImageLoader loader = new ImageLoader(imageURL);
		if (loader.getImage() == null)
			System.err.println("Image "+imageURL+" was not found!");
		return loader.getImage();
	}
	

	public static ImageIcon getIcon(String iconURL) {
		Image img = getImage(iconURL);
		if (img != null)
			return new ImageIcon(img);
		else
			return  new ImageIcon(getImage("images/menus/no.png"));
	}
	
	public static void infoMessage(Component owner, String title, String message) {
		JOptionPane.showMessageDialog(owner, message, title, JOptionPane.INFORMATION_MESSAGE);
	}

	public static boolean confirmDialog(Component owner, String title, String message) {
		int response = JOptionPane.showConfirmDialog(owner, message, title, JOptionPane.YES_NO_OPTION);
		return response == JOptionPane.YES_OPTION;
	}

	public static void errorMessage(Component owner, String title, String message) {
		JOptionPane.showMessageDialog(owner, message, title, JOptionPane.ERROR_MESSAGE);
		System.err.println("[ErrorMessage] "+message);
	}

	
	
	
	public static Image getScaledImage(String imagePath, int maxWidth, int maxHeight) {
		Image img = GuiUtils.getImage(imagePath);
		return getScaledImage(img, maxWidth, maxHeight, false);
	}

	/**
	 * This method scales a given image according to the maximum value of width and height given
	 * @param originalImage The image to be scaled
	 * @param maxWidth The maximum allowed width for the image
	 * @param maxHeight The maximum allowed height for the image
	 * @param mayDistort Selects whether the image may be distorted (in case maxWidth/maxHeight != imgWidth/imgHeight)
	 * @return
	 */
	public static Image getScaledImage(Image originalImage, int maxWidth, int maxHeight, boolean mayDistort) {
		if (originalImage == null)
			return null;

		if (mayDistort)
			return originalImage.getScaledInstance(maxWidth, maxHeight, Image.SCALE_FAST);
		else {
			double imgRatio = (double)originalImage.getWidth(null) / (double)originalImage.getHeight(null);
			double desiredRatio = (double) maxWidth / (double) maxHeight;
			int width = maxWidth;
			int height = maxHeight;

			if (desiredRatio > imgRatio) {
				height = maxHeight;
				width = (int) (maxHeight * imgRatio);
			}
			else {
				width = maxWidth;
				height = (int) (maxWidth / imgRatio);
			}

			//System.out.println("width: "+width+", height: "+height);
			return originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
		}
	}
	
	
	 static public String getFileExtension(File fx) {
	    	String path = null;
	    	try{  
	    		path  = fx.getCanonicalPath(); 
	    	} 
	    	catch (IOException e1){
	    		path = fx.getAbsolutePath();
	    	}
	    	int lastDotPostion = path.lastIndexOf('.');
			return (lastDotPostion != -1)?(path.substring(lastDotPostion + 1)):"";    	
	    }
	 
	public static FileFilter getCustomFileFilter(String desc, String[] validExtensions) {
		final String d = desc;
		final String[] ext = validExtensions;
		return new FileFilter() {
			@Override
			public boolean accept(File f) {		
				if (f.isDirectory())
					return true;
				String extension = getFileExtension(f);
				for (String e : ext) {
					if (e.equalsIgnoreCase(extension) || e.equals("*"))
						return true;
				}
				return false;
			}
			
			@Override
			public String getDescription() {
				return d;
			}
		};		
	}
	
    /**
     * Convenience method that returns a scaled instance of the
     * provided {@code BufferedImage}.
     *
     * @param img the original image to be scaled
     * @param targetWidth the desired width of the scaled instance,
     *    in pixels
     * @param targetHeight the desired height of the scaled instance,
     *    in pixels
     * @param hint one of the rendering hints that corresponds to
     *    {@code RenderingHints.KEY_INTERPOLATION} (e.g.
     *    {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
     *    {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
     *    {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
     * @param higherQuality if true, this method will use a multi-step
     *    scaling technique that provides higher quality than the usual
     *    one-step technique (only useful in downscaling cases, where
     *    {@code targetWidth} or {@code targetHeight} is
     *    smaller than the original dimensions, and generally only when
     *    the {@code BILINEAR} hint is specified)
     * @return a scaled version of the original {@code BufferedImage}
     */
    public BufferedImage getScaledInstance(BufferedImage img,
                                           int targetWidth,
                                           int targetHeight,
                                           Object hint,
                                           boolean higherQuality)
    {
        int type = (img.getTransparency() == Transparency.OPAQUE) ?
            BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage ret = (BufferedImage)img;
        int w, h;
        if (higherQuality) {
            // Use multi-step technique: start with original size, then
            // scale down in multiple passes with drawImage()
            // until the target size is reached
            w = img.getWidth();
            h = img.getHeight();
        } else {
            // Use one-step technique: scale directly from original
            // size to target size with a single drawImage() call
            w = targetWidth;
            h = targetHeight;
        }
        
        do {
            if (higherQuality && w > targetWidth) {
                w /= 2;
                if (w < targetWidth) {
                    w = targetWidth;
                }
            }

            if (higherQuality && h > targetHeight) {
                h /= 2;
                if (h < targetHeight) {
                    h = targetHeight;
                }
            }

            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();

            ret = tmp;
        } while (w != targetWidth || h != targetHeight);

        return ret;
    }
    
    
 // This method returns a buffered image with the contents of an image
    public static BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage)image;
        }
    
        // This code ensures that all the pixels in the image are loaded
        image = new ImageIcon(image).getImage();
    
        // Determine if the image has transparent pixels; for this method's
        // implementation, see e661 Determining If an Image Has Transparent Pixels
        //boolean hasAlpha = hasAlpha(image);
    
        // Create a buffered image with a format that's compatible with the screen
        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            // Determine the type of transparency of the new buffered image
            int transparency = Transparency.OPAQUE;
           transparency = Transparency.BITMASK;
           
            // Create the buffered image
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(
                image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
            // The system does not have a screen
        }
    
        if (bimage == null) {
            // Create a buffered image using the default color model
            int type = BufferedImage.TYPE_INT_ARGB;
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }
    
        // Copy image to buffered image
        Graphics g = bimage.createGraphics();
    
        // Paint the image onto the buffered image
        g.drawImage(image, 0, 0, null);
        g.dispose();
    
        return bimage;
    }
    
    
    public static void reactEnterKeyPress(JButton btn){
        btn.registerKeyboardAction(
          btn.getActionForKeyStroke(
            KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false)), 
            KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), 
            JComponent.WHEN_IN_FOCUSED_WINDOW
          );

        btn.registerKeyboardAction(
          btn.getActionForKeyStroke(
            KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true)), 
            KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), 
            JComponent.WHEN_IN_FOCUSED_WINDOW
          );
      }
    
    public static void reactEscapeKeyPress(JButton btn){
        btn.registerKeyboardAction(
          btn.getActionForKeyStroke(
            KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false)), 
            KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), 
            JComponent.WHEN_IN_FOCUSED_WINDOW
          );

        btn.registerKeyboardAction(
          btn.getActionForKeyStroke(
            KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true)), 
            KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), 
            JComponent.WHEN_IN_FOCUSED_WINDOW
          );
      }
    
    public static Component[] getComponentsOfType(Container parent, Class clazz) {
    	Vector<Component> resp = new Vector<Component>();
    	for (int i = 0; i < parent.getComponentCount(); i++) {
    		
    		if (parent.getComponent(i).getClass().equals(clazz)) {
    			resp.add(parent.getComponent(i));
    		}    		    		
    	}
    	return resp.toArray(new Component[0]);
    }
}
